package com.h2cloud.JUCTest.patten;

import com.h2cloud.JUCTest.TimeUtil.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * @Package: com.h2cloud.JUCTest.patten
 * @ClassName: TestGuardedObjectV3V3
 * @Author: h2cloud
 * @CreateTime: 2022/5/29 5:20 下午
 * @Description:
 */
@Slf4j(topic = "c.TestGuardedObjectV3V3")
public class TestGuardedObjectV3 {
	public static void main(String[] args) throws InterruptedException {
		for (int i = 0; i < 3; i++) {
			new People().start();
		}
		Sleeper.sleep(1);
		for (Integer id : Mailboxes.getIds()) {
			new Postman(id, "内容" + id).start();
		}
	}
}

@Slf4j(topic = "c.People")
class People extends Thread {
	@Override
	public void run() {
		// 收信
		GuardedObjectV3 GuardedObjectV3 = Mailboxes.createGuardedObjectV3();
		log.debug("开始收信 id:{}", GuardedObjectV3.getId());
		Object mail = GuardedObjectV3.get(5000);
		log.debug("收到信 id:{}, 内容:{}", GuardedObjectV3.getId(), mail);
	}
}

@Slf4j(topic = "c.Postman")
class Postman extends Thread {
	private int id;
	private String mail;

	public Postman(int id, String mail) {
		this.id = id;
		this.mail = mail;
	}

	@Override
	public void run() {
		GuardedObjectV3 GuardedObjectV3 = Mailboxes.getGuardedObjectV3(id);
		log.debug("送信 id:{}, 内容:{}", id, mail);
		GuardedObjectV3.complete(mail);
	}
}

class Mailboxes {
	private static Map<Integer, GuardedObjectV3> boxes = new Hashtable<>();

	private static int id = 1;

	// 产生唯一 id
	private static synchronized int generateId() {
		return id++;
	}

	public static GuardedObjectV3 getGuardedObjectV3(int id) {
		//根据id获取到box，并删除对应的key和value,避免堆内存爆了
		return boxes.remove(id);
	}

	public static GuardedObjectV3 createGuardedObjectV3() {
		GuardedObjectV3 go = new GuardedObjectV3(generateId());
		boxes.put(go.getId(), go);
		return go;
	}

	public static Set<Integer> getIds() {
		return boxes.keySet();
	}
}

// 增加超时效果
class GuardedObjectV3 {

	// 标识 Guarded Object
	private int id;

	public GuardedObjectV3(int id) {
		this.id = id;
	}

	public int getId() {
		return id;
	}

	// 结果
	private Object response;

	// 获取结果
	// timeout 表示要等待多久 2000
	public Object get(long timeout) {
		synchronized (this) {
			// 开始时间 15:00:00
			long begin = System.currentTimeMillis();
			// 经历的时间
			long passedTime = 0;
			while (response == null) {
				// 这一轮循环应该等待的时间
				long waitTime = timeout - passedTime;
				// 经历的时间超过了最大等待时间时，退出循环
				if (timeout - passedTime <= 0) {
					break;
				}
				try {
					this.wait(waitTime); // 虚假唤醒 15:00:01
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 求得经历时间
				passedTime = System.currentTimeMillis() - begin; // 15:00:02  1s
			}
			return response;
		}
	}

	// 产生结果
	public void complete(Object response) {
		synchronized (this) {
			// 给结果成员变量赋值
			this.response = response;
			this.notifyAll();
		}
	}
}

